#include <cuda_runtime.h>

#include "system_typ.h"
#include "util.h"

// Инициализация.
void dvmh_init_();

// Запрос на актуализацию данных на хост-системе. Первый параметр – заголовочный массив. Второй – массив нижних индексов. Третий – массив верхних индексов. Значение индекса -2147483648 означает открытую границу (то есть до конца конкретного измерения с конкретной стороны имеющегося у этого процесса части пространства массива (локальная часть + теневые грани + чтобы-то-ни-было)).
void dvmh_get_actual_subarray_(long dvmDesc[], long lowIndex[], long highIndex[]);

// Запрос на актуализацию данных на хост-системе. Параметр – скалярная переменная.
void dvmh_get_actual_scalar_(void *addr);

// Запрос на актуализацию на хост-систему граничных элементов.
DvmhShadowRef dvmh_get_actual_edges_(ShadowGroupRef *group);

// Объявление актуальности данных на хост-системе. Первый параметр – заголовочный массив. Второй – массив нижних индексов. Третий – массив верхних индексов. Значение индекса -2147483648 означает открытую границу (то есть до конца конкретного измерения с конкретной стороны имеющейся у этого процесса локальной части массива).
void dvmh_actual_subarray_(long dvmDesc[], long lowIndex[], long highIndex[]);

// Объявление актуальности данных на хост-системе. Параметр – скалярная переменная.
void dvmh_actual_scalar_(void *addr);

// Инициация обновления теневых граней на вычислителях региона. Первый параметр – ссылка на регион, которую вернул вызов region_create_(). Второй параметр – то, что вернул соответствующий прошлый вызов dvmh_get_actual_edges_().
void dvmh_shadow_renew_(DvmhRegionRef *regionRef, DvmhShadowRef *group);

// Вычисляет естественную базу для массива. Это такая база, при использовании которой смещение равно нулю.
void *dvmh_get_natural_base_(long *deviceRef, long dvmDesc[]);

// Вычисляет смещение представителя скалярной переменной на заданном устройстве по заданной базе. Параметры: устройство(будет передаваться в обработчик системой поддержки), база, переменная скалярная.
long dvmh_calculate_offset_(long *deviceRef, void *base, void *variable);

// Заполняет урезанный DVM-заголовочный массив для представителя на заданном устройстве в соответствии с указанной базой. Параметры: устройство(будет передаваться в обработчик системой поддержки), база, заголовочный массив, массив для вновь заполненного заголовка для конкретного устройства.
void dvmh_fill_header_(long *deviceRef, void *base, long dvmDesc[], long dvmhDesc[]);

// Оповещение о фактическом прекращении существования переменной-массива. Вызывать непосредственно перед выходом из подпрограммы для локальных переменных, а также перед deallocate и redistribute.
void dvmh_destroy_array_(long dvmDesc[]);

// Оповещение о фактическом прекращении существования переменной-скаляра. Вызывать непосредственно перед выходом из подпрограммы для локальных переменных, а также перед deallocate.
void dvmh_destroy_scalar_(void *addr);

// Регионы
// Создание региона. Параметр – флаг асинхронности
DvmhRegionRef region_create_(long *asyncFlagRef);

// Регистрация подмассива в регионе. Первый параметр – ссылка на регион, которую вернул вызов region_create_(). Второй параметр – направление использования (1 – in, 2 – out, 4 – local, 3 – inout, 5 - inlocal). Третий параметр – заголовочный массив. Четвертый параметр – массив нижних индексов. Пятый параметр – массив верхних индексов. Значение индекса -2147483648 означает открытую границу (то есть до конца конкретного измерения с конкретной стороны (для in - локальная часть + теневые грани, для out и local - только локальная часть)).
void region_register_subarray_(DvmhRegionRef *regionRef, long *intentRef, long dvmDesc[], long lowIndex[], long highIndex[]);

// Регистрация скаляра в регионе. Первый параметр – ссылка на регион, которую вернул вызов region_create_(). Второй параметр – направление использования (1 – in, 2 – out, 4 – local, 3 – inout, 5 - inlocal). Третий параметр – скалярная переменная. Четвертый параметр – размер скаляра (4 для float, 8 для double, и т.п.).
void region_register_scalar_(DvmhRegionRef *regionRef, long *intentRef, void *addr, long *sizeRef);

// Оповещение о наборе типов устройств, для которых подготовлен регион. Первый параметр - ссылка на регион, которую вернул вызов region_create_(). Второй параметр – побитовое объединение типов устройств (DEVICE_TYPE_HOST = 1, DEVICE_TYPE_CUDA = 2).
void region_prepared_for_devices_(DvmhRegionRef *regionRef, long *devicesRef);

// Старт вычислительной части региона. Если регион пустой, то эту функцию не надо вызывать.
void region_inner_start_(DvmhRegionRef *regionRef);

// Создает нечто для управления параллельным DVMH циклом. Первый параметр – ссылка на регион, которую вернул region_create_(). Второй параметр – DVM-описатель параллельного цикла.
DvmhLoopRef loop_create_(DvmhRegionRef *regionRef, LoopRef *InDvmLoop);

// Функция для включения редукции в параллельный DVMH цикл. Первый параметр – ссылка на нечто для управления параллельным DVMH циклом. Второй параметр – ссылка на DVMовскую редукционную функцию.
void loop_insred_(DvmhLoopRef *InDvmhLoop, RedRef *InRedRefPtr);

// Установка размера блока нитей. Первый параметр - ссылка на нечто для управления параллельным DVMH циклом. Второй параметр – размер блока нитей по оси X. Третий параметр - размер блока нитей по оси Y. Четвертый параметр - размер блока нитей по оси Z. Если размер по какой-то из осей не задан пользователем, то передавать в качестве соответствующего размера следует 1.
void loop_set_cuda_block_(DvmhLoopRef *InDvmhLoop, long *InXRef, long *InYRef, long *InZRef);

// Регистрация обработчика для параллельного цикла. Первый параметр - ссылка на нечто для управления параллельным DVMH циклом. Второй параметр - тип устройства, для которого годится данный обработчик (DEVICE_TYPE_HOST, ...). Третий параметр - набор флагов, указывающий характеристики обработчика (HANDLER_TYPE_PARALLEL = 1, HANDLER_TYPE_MASTER = 2). Четвертый параметр - ссылка на функцию-обработчик. Пятый параметр - количество передаваемых в обработчик базовых массивов. Шестой параметр - количество "пользовательских" параметров, которые необходимо передать обработчику. Седьмой и последующие - эти самые параметры (передача строго по адресу).
// HANDLER_TYPE_PARALLEL означает, что обработчик является параллельным и для него на усмотрение системы поддержки будет выделено несколько (1 или более) слотов исполнения для устройства. Такой обработчик должен сам запросить у системы поддержки свое количество слотов (функция loop_get_slot_number_()).
// HANDLER_TYPE_MASTER означает, что обработчик может исполняться только в основной нити хост-системы (в частности это влечет за собой, что других его инстанций в параллель запущено не будет). Этот фдаг ставится в случае использования технологий, не готовых к внешнему параллелизму.
void loop_register_handler_(DvmhLoopRef *InDvmhLoop, long *deviceTypeRef, long *flagsRef, void (*f)(), long *basesCount, long *paramCount, ...);

// Указание начала исполнения цикла. Параметр - ссылка на нечто для управления параллельным DVMH циклом.
void loop_start_(DvmhLoopRef *InDvmhLoop);

// Эти функции вызываются из отдельных подпрограмм (обработчиков), а в основной программе нет баз кроме DVMовских и вообще нет следов CUDA.
// Запрос на заполнение границ цикла (и шагов) для исполнения порции цикла обработчиком. Первый параметр - новый локальный описатель параллельного DVMH цикла, который передается параметром в обработчик.
void loop_fill_bounds_(DvmhLoopRef *InDvmhLoop, IndexType lowIndex[], IndexType highIndex[], IndexType stepIndex[]);

// Запрос на заполнение редукционного массива и сопутствующего ему loc начальными данными. Первый параметр - новый локальный описатель параллельного DVMH цикла, который передается параметром в обработчик. Второй параметр – номер по порядку редукционной функции для цикла. Третий параметр – указатель на локальный для обработчика экземпляр редукционного массива. Четвертый параметр - указатель на локальный для обработчика экземпляр массива loc.
void loop_red_init(DvmhLoopRef *InDvmhLoop, int InRedNum, void *arrayPtr, void *locPtr);
// Фортрановская версия
void loop_red_init_(DvmhLoopRef *InDvmhLoop, long *InRedNumRef, void *arrayPtr, void *locPtr);

// Функция для регистрации переменных, в которые будут помещены смещения для редукционной переменной и ее LOC. Первый параметр – новый локальный описатель параллельного DVMH цикла, который передается параметром в обработчик. Второй параметр – номер по порядку редукционной функции для цикла (нумерация с нуля). Третий параметр – база на девайсе для редукционной переменной. Четвертый параметр – база на девайсе для переменной LOC. Пятый параметр – переменная, в которую будет записано смещение для редукционной переменной. Шестой параметр – адрес переменной, в которую будет записано смещение для LOC, если LOC нет, то передавать нуль (dvm0c0).
void loop_register_red_cuda_(DvmhLoopRef *InDvmhLoop, long *InRedNumRef, void *InDeviceArrayBaseAddr, void *InDeviceLocBaseAddr, CudaOffsetTypeRef *ArrayOffsetPtr,
        CudaOffsetTypeRef *LocOffsetPtr);
// Сишный безбазовый и бессмещенческий вариант. В ArrayPtr и LocPtr будут положены (в процессе итерирования цикла) адреса в памяти устройства, в которые ядро будет записывать информацию из блоков.
void loop_register_red_cuda(DvmhLoopRef *InDvmhLoop, int InRedNum, void **ArrayPtr, void **LocPtr);

// Вызов для итерирования вызовов ядер на ГПУ. Возвращает 1, если надо запустить ядро после этого вызова и 0, если надо заканчивать. Первый параметр – новый локальный описатель параллельного DVMH цикла, который передается параметром в обработчик. Второй параметр – переменная типа dim3, в которую будут записаны размеры решетки блоков для запуска ядра. Третий параметр – переменная типа dim3, в которую будут записаны размеры блока нитей для запуска ядра. Четвертый параметр - переменна типа cudaStream_t, в которую будет записан идентификатор CUDA-потока для запуска. Пятый параметр – база для адресации информации о блоках (ее тип - IndexType). Шестой параметр – переменная, в которую будет записано смещение информации о блоках относительно базы.
long loop_do_cuda_(DvmhLoopRef *InDvmhLoop, dim3 *OutBlocks, dim3 *OutThreads, cudaStream_t *OutStream, void *InDeviceBaseAddr, CudaOffsetType *InOutBlocksOffs);
// Сишный безбазовый и бессмещенческий вариант. В InOutBlocks будет записан адрес на устройстве, где располагается информация для блоков.
long loop_do_cuda(DvmhLoopRef *InDvmhLoop, dim3 *OutBlocks, dim3 *OutThreads, cudaStream_t *OutStream, IndexType **InOutBlocks);

// Возврат результатов частичной редукции в РТС. Первый параметр - новый локальный описатель параллельного DVMH цикла, который передается параметром в обработчик. Второй параметр – номер по порядку редукционной функции для цикла. Третий и четвертый - результаты работы обработчика порции цикла.
void loop_red_post_(DvmhLoopRef *InDvmhLoop, long *InRedNumRef, void *arrayPtr, void *locPtr);

// Конец цикла. Параметр – ссылка на нечто для управления параллельным DVMH циклом.
void loop_end_(DvmhLoopRef *loopRef);

// Конец региона. Параметр – ссылка на регион, которую вернул вызов region_create_().
void region_end_(DvmhRegionRef *regionRef);
